package sdiff.tests;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.HashSet;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;

import junit.framework.TestCase;

import org.xml.sax.SAXException;


/**
 * Runs LDiff and SDiff against Reiss's test suite (ClideDatabaseManager)
 * and also against our new Eclipse test suite.
 * 
 * @author jspacco
 *
 */
public class Scam2009Tests extends TestCase
{
    public static final String ldiffExecutable=
        "files/scripts/ldiff.pl -o ext";
    public static final String limboDir=
        "files/limbodata";
    
    
    static void writeToFile(String filename, String output)
    throws IOException
    {
        PrintStream out=new PrintStream(filename);
        out.println(output);
        out.flush();
        out.close();
    }

    /**
     * Run all of the LDiff configurations against all test cases.
     * XXX Warning:  This will take a really, really, really long time.
     * Many hours or several days.
     * @throws Exception
     */
    public void testAllLdiffs()
    throws Exception
    {
        long timestamp=System.currentTimeMillis();
        for (String test : new String[] {
                "ASTResolving",
                "ArrayReference",
                "BaseTypes",
                "BuildPathsPropertyPage",
                "CPListLabelProvider",
                "CompilationUnitDocumentProvider",
                "DeltaProcessor",
                "DialogCustomize",
                "DirectoryDialog",
                "DoubleCache",
                "FontData",
                "GC",
                "GC2",
                "JavaCodeScanner",
                "JavaModelManager",
                "JavaPerspectiveFactory",
                "PluginSearchScope",
                "RefreshLocal",
                "ResourceCompareInput",
                "ResourceInfo",
                "SaveManager",
                "TabFolder",
                "ClideDatabaseManager"
        }) {
            String fullpath=limboDir+File.separator+test+".xml";
            String xmlOutfile=limboDir+"/results/"+test+"-ldiff-"+timestamp+".xml";
            String textOutfile=limboDir+"/results/"+test+"-ldiff-"+timestamp+".txt";

            runAllLdiffConfigurations(ldiffExecutable, 
                    fullpath, 
                    textOutfile,
                    false, 
                    null);
        }
        
    }

    /**
     * This runs all of the SDiff configurations against all
     * of the test cases.  The results go into a results directory
     * inside the directory with the test cases.
     * @throws Exception
     */
    public void testSDiffOnAllTests()
    throws Exception
    {
        long timestamp=System.currentTimeMillis();
        for (String f : new String[] {
                "ASTResolving",
                "ArrayReference",
                "BaseTypes",
                "BuildPathsPropertyPage",
                "CPListLabelProvider",
                "CompilationUnitDocumentProvider",
                "DeltaProcessor",
                "DialogCustomize",
                "DirectoryDialog",
                "DoubleCache",
                "FontData",
                "GC",
                "GC2",
                "JavaCodeScanner",
                "JavaModelManager",
                "JavaPerspectiveFactory",
                "PluginSearchScope",
                "RefreshLocal",
                "ResourceCompareInput",
                "ResourceInfo",
                "SaveManager",
                "TabFolder",
                "ClideDatabaseManager"
        })
        {
            String filename=limboDir+"/"+f+".xml";
            String xmlOutfile=limboDir+"/results/"+f+"-sdiff-" +timestamp+".xml";
            String textOutfile=limboDir+"/results/"+f+"-sdiff-" +timestamp+".txt";

            String javaVersion="1.5";
            if (f.equals("SaveManager")) {
                // needs to be compiled with 1.4
                javaVersion="1.4";
            }

            PrintStream textOut=new PrintStream(textOutfile);
            
            for (Boolean[] arr : new Boolean[][] {
                    // leven
                    {false, false, false, false},
                    {false, false, true, false},
                    {false, false, true, true},
                    
                    // token
                    {false, true, false, false},
                    {false, true, true, false},
                    {false, true, true, true},
                    
                    // min(leven, token)
                    {true, false, false, false},
                    {true, false, true, false},
                    {true, false, true, true},
            })
            {
                boolean useTokenEditDistance=arr[0];
                boolean useMinEditDistance=arr[1];
                boolean useProbable=arr[2];
                boolean usePossible=arr[3];

                System.err.println("sdiff " +useTokenEditDistance+" "+
                        useMinEditDistance+" "+
                        useProbable+" "+
                        usePossible);
                // Regular configuration
                runSDiff(javaVersion,
                        filename, 
                        textOut, 
                        useTokenEditDistance,
                        useMinEditDistance,
                        useProbable,
                        usePossible,
                        false, 
                        xmlOutfile);
                System.err.println("done with " +f);
            }
            textOut.flush();
            textOut.close();
        }
    }

    void runSDiff(
            String javaVersion,
            String configFile, 
            PrintStream textOut,
            boolean useTokenEditDistance,
            boolean useMinEditDistance,
            boolean useProbable,
            boolean usePossible,
            boolean fullXMLdump,
            String xmlOutfile)
    throws IOException, SAXException, ParserConfigurationException
    {
        String configName="SDiff";
        if (useMinEditDistance)
            configName+="-min";
        else if (useTokenEditDistance)
            configName+="-token";
        if (usePossible)
            configName+="-possible";
        if (useProbable)
            configName+="-probable";

        File file=new File(configFile);
        ReissConfig r=new ReissConfig(configFile);
        ReissResultsCollection allResults=new ReissResultsCollection(r);

        r.sDiffAndScoreResults(configName, 
                useTokenEditDistance, useMinEditDistance,
                useProbable, usePossible, 
                file.getParent(), javaVersion,
                allResults);
        //String data=allResults.getOverallScore(configName).toStringPercentage();
        String data=allResults.getOverallScore(configName).toString();
        String line=configName+"\t"+data+"\t"+allResults.getAverageTimeForMethod(configName);
        textOut.println(line);
        textOut.flush();

        // Write XML output to file
        if (fullXMLdump) {
            writeToFile(xmlOutfile, allResults.toXMLString());
        }
    }


    public static void runAllLdiffConfigurations(
            String ldiffExecutable,
            String configFile,
            String textOutfile,
            boolean fullXMLdump,
            String xmlOutfile)
    throws Exception
    {
        File file=new File(configFile);

        ReissConfig r=new ReissConfig(configFile);
        ReissResultsCollection allResults=new ReissResultsCollection(r);
        PrintStream out=new PrintStream(textOutfile);

        /*
         * Use LDiff configuration settings:
         * 
         * -i n (0 for line similarity only, 1 for hunk similarity, > 1 not sure?)
         * -HT CUT:N (CUT is the similarity threshold for hunks, default=0.5
         *              N is the number of hunks to compare to, default N=all
         * -LT THR (line similarity threshold, default 0.4)
         * -lm (line similarity metric, default leven)
         * -hm (hunk similarity metric, default cosine)
         *      similarity metrics are: leven, cosine, jaccard, overlap
         * -lt (line tokenizer)
         * -ht (hunk tokenizer)
         *      tokenizers can be char or word
         */

        int x=0;
        Set<String> configs=new HashSet<String>();

        for (String w: new String[] {"", "-w"}) {
            //for (String i : new String[] {"0", "1", "2", "3", "4", "5"}) {
            for (String i : new String[] {"3", "4"}) {
                //for (String LT: new String[] {"0.35", "0.4", "0.5", "0.6"}) {
                //for (String lm: new String[] {"leven", "cosine", "jaccard", "overlap"}) {
                //for (String lm: new String[] {"leven", "cosine" }) {
                for (String lm: new String[] {"jaccard" }) {
                    //for (String hm: new String[] {"leven", "cosine", "jaccard", "overlap"}) {
                    for (String hm: new String[] {"leven", "cosine"}) {
                        for (String lt: new String[] {"char", "word"}) {
                            //for (String ht: new String[] {"char", "word"}) {
                            for (String ht: new String[] {"word"}) {
                                x++;
                                String c=w;
                                c+=" -i "+i;
                                //                                    if (i.equals("1")) {
                                //                                        // -hm and -ht only make sense with 1
                                c+=" -hm "+hm;
                                c+=" -ht "+ht;
                                //                                    }
                                //c+=" -LT "+LT;
                                c+=" -lm "+lm;
                                c+=" -lt "+lt;
                                configs.add(c);
                            }
                        }
                    }
                }
            }
        }
        //            }
        //        }
        System.out.println(x);
        System.out.println(configs.size());


        int z=1;
        for (String o: configs) {
            LdiffLineMappings ldiff1=new LdiffLineMappings(ldiffExecutable);
            ldiff1.addOption(o);
            r.ldiffAndScoreResults(ldiff1, 
                    file.getParent(), 
                    allResults);
            // always write to the text output file
            String data=allResults.getOverallScore(ldiff1.getConfigName()).toString();
            String line=ldiff1.getConfigName()+"\t"+data+"\t"+allResults.getAverageTimeForMethod(ldiff1.getConfigName());
            out.println(line);
            out.flush();
            System.err.println("done with config #"+z+" "+ldiff1.getConfigName());
            z++;
            //            if (z > 5)
            //                break;
        }
        out.flush();
        out.close();

        if (fullXMLdump) {
            writeToFile(xmlOutfile, allResults.toXMLString());
        }
    }
}
